#ifndef BHO_DESCRIBE_DETAIL_PP_UTILITIES_HPP_INCLUDED
#define BHO_DESCRIBE_DETAIL_PP_UTILITIES_HPP_INCLUDED

// Copyright 2021 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#define BHO_DESCRIBE_PP_EXPAND(x) x

#define BHO_DESCRIBE_PP_CAT(x, y) BHO_DESCRIBE_PP_CAT_I(x, y)
#define BHO_DESCRIBE_PP_CAT_I(x, ...) x ## __VA_ARGS__

#if defined(_MSC_VER) && !defined(__clang__)

#define BHO_DESCRIBE_PP_FIRST(x) BHO_DESCRIBE_PP_FIRST_I((x))
#define BHO_DESCRIBE_PP_FIRST_I(x) BHO_DESCRIBE_PP_FIRST_II x
#define BHO_DESCRIBE_PP_FIRST_II(x, ...) x

#else

#define BHO_DESCRIBE_PP_FIRST(x) BHO_DESCRIBE_PP_FIRST_I(x)
#define BHO_DESCRIBE_PP_FIRST_I(x, ...) x

#endif

#define BHO_DESCRIBE_PP_IS_PAREN_I(x) BHO_DESCRIBE_PP_CAT(BHO_DESCRIBE_PP_IS_PAREN_I_, BHO_DESCRIBE_PP_IS_PAREN_II x)
#define BHO_DESCRIBE_PP_IS_PAREN_II(...) 0
#define BHO_DESCRIBE_PP_IS_PAREN_I_0 1,
#define BHO_DESCRIBE_PP_IS_PAREN_I_BHO_DESCRIBE_PP_IS_PAREN_II 0,

#define BHO_DESCRIBE_PP_IS_PAREN(x) BHO_DESCRIBE_PP_FIRST(BHO_DESCRIBE_PP_IS_PAREN_I(x))

#define BHO_DESCRIBE_PP_EMPTY

#define BHO_DESCRIBE_PP_IS_EMPTY(x) BHO_DESCRIBE_PP_IS_EMPTY_I(BHO_DESCRIBE_PP_IS_PAREN(x), BHO_DESCRIBE_PP_IS_PAREN(x BHO_DESCRIBE_PP_EMPTY ()))
#define BHO_DESCRIBE_PP_IS_EMPTY_I(x, y) BHO_DESCRIBE_PP_IS_EMPTY_II(x, y)
#define BHO_DESCRIBE_PP_IS_EMPTY_II(x, y) BHO_DESCRIBE_PP_IS_EMPTY_III(x, y)
#define BHO_DESCRIBE_PP_IS_EMPTY_III(x, y) BHO_DESCRIBE_PP_IS_EMPTY_III_ ## x ## y
#define BHO_DESCRIBE_PP_IS_EMPTY_III_00 0
#define BHO_DESCRIBE_PP_IS_EMPTY_III_01 1
#define BHO_DESCRIBE_PP_IS_EMPTY_III_10 0
#define BHO_DESCRIBE_PP_IS_EMPTY_III_11 0

#define BHO_DESCRIBE_PP_CALL(F, a, x) BHO_DESCRIBE_PP_CAT(BHO_DESCRIBE_PP_CALL_I_, BHO_DESCRIBE_PP_IS_EMPTY(x))(F, a, x)
#define BHO_DESCRIBE_PP_CALL_I_0(F, a, x) F(a, x)
#define BHO_DESCRIBE_PP_CALL_I_1(F, a, x)

#define BHO_DESCRIBE_PP_PARSE(x) BHO_DESCRIBE_PP_CAT(BHO_DESCRIBE_PP_PARSE_I_, BHO_DESCRIBE_PP_PARSE_II x)
#define BHO_DESCRIBE_PP_PARSE_II(...) 0, (__VA_ARGS__),
#define BHO_DESCRIBE_PP_PARSE_I_BHO_DESCRIBE_PP_PARSE_II 0, ~,
#define BHO_DESCRIBE_PP_PARSE_I_0 1

#if defined(_MSC_VER) && !defined(__clang__)

#define BHO_DESCRIBE_PP_NAME(x) BHO_DESCRIBE_PP_NAME_I(BHO_DESCRIBE_PP_PARSE(x))
#define BHO_DESCRIBE_PP_NAME_I(x) BHO_DESCRIBE_PP_NAME_II((x))
#define BHO_DESCRIBE_PP_NAME_II(x) BHO_DESCRIBE_PP_NAME_III x
#define BHO_DESCRIBE_PP_NAME_III(x, y, z) #z

#else

#define BHO_DESCRIBE_PP_NAME(x) BHO_DESCRIBE_PP_NAME_I(BHO_DESCRIBE_PP_PARSE(x))
#define BHO_DESCRIBE_PP_NAME_I(x) BHO_DESCRIBE_PP_NAME_II(x)
#define BHO_DESCRIBE_PP_NAME_II(x, y, z) #z

#endif

// template<class C, class F> constexpr auto mfn( F C::* p ) { return p; }
// template<class C, class F> constexpr auto mfn( F * p ) { return p; }

#define BHO_DESCRIBE_PP_POINTER(C, x) BHO_DESCRIBE_PP_POINTER_I(C, BHO_DESCRIBE_PP_PARSE(x))

#define BHO_DESCRIBE_PP_EXPAND_V(...) __VA_ARGS__

#if defined(_MSC_VER) && !defined(__clang__)

#define BHO_DESCRIBE_PP_POINTER_I(C, x) BHO_DESCRIBE_PP_POINTER_II((C, x))
#define BHO_DESCRIBE_PP_POINTER_II(x) BHO_DESCRIBE_PP_POINTER_III x
#define BHO_DESCRIBE_PP_POINTER_III(C, x, y, z) BHO_DESCRIBE_PP_POINTER_III_##x(C, y, z)
#define BHO_DESCRIBE_PP_POINTER_III_0(C, y, z) &C::z
#define BHO_DESCRIBE_PP_POINTER_III_1(C, y, z) ::bho::describe::detail::mfn<C, BHO_DESCRIBE_PP_EXPAND_V y>(&C::z)

#else

#define BHO_DESCRIBE_PP_POINTER_I(C, x) BHO_DESCRIBE_PP_POINTER_II(C, x)
#define BHO_DESCRIBE_PP_POINTER_II(C, x, y, z) BHO_DESCRIBE_PP_POINTER_III_##x(C, y, z)
#define BHO_DESCRIBE_PP_POINTER_III_0(C, y, z) &C::z
#define BHO_DESCRIBE_PP_POINTER_III_1(C, y, z) ::bho::describe::detail::mfn<C, BHO_DESCRIBE_PP_EXPAND_V y>(&C::z)

#endif

#endif // #ifndef BHO_DESCRIBE_DETAIL_PP_UTILITIES_HPP_INCLUDED
